#include <iostream>
#include <pthread.h>
#include <queue>
#include <unistd.h>
#include <signal.h>


template <class T>
class BlockQueue 
{
public:
    BlockQueue(size_t capacity)  
        : capacity_(capacity)
    {
        pthread_mutex_init(&mutex_, NULL);
        pthread_cond_init(&not_empty_, NULL);
        pthread_cond_init(&not_full_, NULL);
    }

    void push(const T& item) 
    {
        pthread_mutex_lock(&mutex_);

        // 队列满，等待唤醒
        while (q_.size() >= capacity_) {
            pthread_cond_wait(&not_full_, &mutex_);
        }

        q_.push(item);

        pthread_cond_signal(&not_empty_);
        pthread_mutex_unlock(&mutex_);
    }

    T pop() 
    {
        pthread_mutex_lock(&mutex_);

        while (q_.size() == 0) {
            pthread_cond_wait(&not_empty_, &mutex_);
        }

        T item = q_.front();
        q_.pop();

        pthread_cond_signal(&not_full_);
        pthread_mutex_unlock(&mutex_);

        return item;
    }   

    ~BlockQueue()
    {
        pthread_mutex_destroy(&mutex_);
        pthread_cond_destroy(&not_empty_);
        pthread_cond_destroy(&not_full_);
    }

private:
    std::queue<T> q_;
    size_t capacity_;
    pthread_mutex_t mutex_;
    pthread_cond_t not_empty_;
    pthread_cond_t not_full_;
};


const int PRODUCER_NUMS = 2;
const int CONSUMER_NUMS = 3;

volatile bool flag = true;

typedef struct 
{
    BlockQueue<int>* bq;
    int id;

} producer_t;

typedef struct 
{
    BlockQueue<int>* bq;
    int id;

} consumer_t;

void* produce(void* arg) 
{
    producer_t* p = static_cast<producer_t*>(arg);
    int item = 0;
    while (flag) {
        p->bq->push(item);
        std::cout << "produce-" << p->id << " : " << item++ << std::endl;
        sleep(1);
    }
    std::cout << "producer-" << p->id << " is to quit." << std::endl;
    delete p;
    return NULL;
}

void* consume(void* arg)
{
    consumer_t* c = static_cast<consumer_t*>(arg);

    while (flag) {
        int item = c->bq->pop();
        std::cout << "consume-" << c->id << " : " << item << std::endl;
        sleep(1);
    }
    std::cout << "consumer-" << c->id << " is to quit." << std::endl;
    delete c;
    return NULL;
}

void handleSignal(int sig) 
{
    flag = false;
}

int main()
{
    signal(SIGINT, handleSignal);

    BlockQueue<int>* bq = new BlockQueue<int>(5);

    pthread_t pdrs[PRODUCER_NUMS];
    pthread_t csrs[CONSUMER_NUMS];

    for (int i = 0; i < PRODUCER_NUMS; i++) {
        producer_t* producer = new producer_t();
        producer->bq = bq;
        producer->id = i;

        pthread_create(&pdrs[i], NULL, produce, producer);
        sleep(1);
    }

    for (int i = 0; i < CONSUMER_NUMS; i++) {
        consumer_t* consumer = new consumer_t();
        consumer->bq = bq;
        consumer->id = i;

        pthread_create(&csrs[i], NULL, consume, consumer);
        sleep(1);
    }

    for (int i = 0; i < PRODUCER_NUMS; i++) {
        pthread_join(pdrs[i], NULL);
    }

    for (int i = 0; i < CONSUMER_NUMS; i++) {
        pthread_join(csrs[i], NULL);
    }

    delete bq;

    return 0;
}